package org.net5ijy.cloud.plugin.feign;

import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.net5ijy.cloud.plugin.feign.core.FeignClientScanner;
import org.net5ijy.cloud.plugin.feign.core.model.FeignClassAndModel;
import org.net5ijy.cloud.plugin.feign.core.model.FeignClientClass;
import org.net5ijy.cloud.plugin.feign.core.model.FeignMethod;
import org.net5ijy.cloud.plugin.feign.core.model.FeignMethodArgument;
import org.net5ijy.cloud.plugin.feign.core.model.FeignModel;
import org.net5ijy.cloud.plugin.feign.core.model.FeignModelField;
import org.net5ijy.cloud.plugin.feign.core.model.IJavaFile;
import org.net5ijy.cloud.plugin.feign.core.util.ClazzUtil;
import org.net5ijy.cloud.plugin.feign.core.util.DynamicClasspath;
import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import com.thoughtworks.qdox.model.JavaMethod;

/**
 * FeignClassGenerateUtil
 *
 * @author XGF
 * @date 2020/11/17 14:47
 */
class FeignClassGenerateUtil {

  static FeignClassAndModel getFeignClassAndModel(
      String scanPackage, DynamicClasspath dynamicClasspath) {

    String basedir = FeignAutoGenerator.mavenProject.getBasedir().getAbsolutePath();
    FeignClientScanner scanner = new FeignClientScanner(
        basedir, FeignAutoGenerator.scanPath, dynamicClasspath);

    List<FeignClientClass> list = scanner.scan(scanPackage);

    FeignClassAndModel feignClassAndModel = new FeignClassAndModel();
    for (FeignClientClass feignClientClass : list) {
      for (FeignMethod feignMethod : feignClientClass.getMethods()) {
        for (FeignMethodArgument feignMethodArgument : feignMethod.getArguments()) {
          List<String> l = feignMethodArgument.getImportClass().stream().filter(p -> !p.startsWith("java.lang")).collect(Collectors.toList());
          feignClientClass.getImportClass().addAll(l);
        }
      }
      feignClientClass.setImportClass(new LinkedHashSet<>(feignClientClass.getImportClass().stream().sorted().collect(Collectors.toList())));
      getJavaFileInfo(basedir, feignClientClass, feignClientClass.getSourceClassName());
    }
    feignClassAndModel.setFeignClientClasses(list);
    

    List<FeignModel> models = new ArrayList<>();
    // 处理模型数据
    for (String modelClassName : scanner.getModelClassNames()) {

      try {

        Class<?> clazz = Class.forName(modelClassName, false, dynamicClasspath.getClassLoader());

        FeignModel model = new FeignModel();
        model.setPackageName(modelClassName.substring(0, modelClassName.lastIndexOf(".")));
        model.setModelName(modelClassName.substring(modelClassName.lastIndexOf(".") + 1));
        
        Field[] fields = clazz.getDeclaredFields();
        Class<?> superclass = clazz.getSuperclass();
        if (superclass != null && superclass != Object.class) {
          if (ClazzUtil.isValidImportClass(superclass.getName(), model.getPackageName())) {
            model.getImportClass().add(superclass.getName());
          }
          model.setParentClass(superclass.getSimpleName());
        }

        List<FeignModelField> feignModelFields = new ArrayList<>();

        for (Field field : fields) {
          FeignModelField modelField = new FeignModelField();
          modelField.setName(field.getName());
          modelField.setType(field.getGenericType().getTypeName().replaceAll("([a-z0-9]+\\.)*", ""));
          feignModelFields.add(modelField);
          Set<String> importClass = ClazzUtil.getImportClass(model.getPackageName(), field.getGenericType());
          model.getImportClass().addAll(importClass);
        }

        model.setImportClass(new LinkedHashSet<>(model.getImportClass().stream().sorted().collect(Collectors.toList())));
        model.setModelFields(feignModelFields);

        getJavaFileInfo(basedir, model, modelClassName);
        models.add(model);
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
      }
    }

    feignClassAndModel.setFeignModels(models);
    feignClassAndModel.setServiceName(FeignAutoGenerator.mavenProject.getArtifactId());
    feignClassAndModel.setDescription(FeignAutoGenerator.mavenProject.getDescription());
    feignClassAndModel.setVersion(FeignAutoGenerator.mavenProject.getVersion());

    String groupId = FeignAutoGenerator.mavenProject.getGroupId();
    if (groupId == null) {
      groupId = FeignAutoGenerator.mavenProject.getParent().getGroupId();
    }
    feignClassAndModel.setGroupId(groupId);

    return feignClassAndModel;
  }

  
  static void getJavaFileInfo(String basedir, IJavaFile c, String className) {
    String javaFilePath = className.replace(".", "/") + ".java";
    File file = new File(basedir + "/src/main/java/" + javaFilePath);
    if (file.exists()) {
      JavaProjectBuilder builder =  new  JavaProjectBuilder();
      try {
        builder.addSource(file);
        JavaClass javaClass = builder.getClassByName(className);
        if (null != javaClass.getComment()) {
          c.setComment(javaClass.getComment());
        }
        
        if (c instanceof FeignClientClass) {
          FeignClientClass clazz = (FeignClientClass)c;
          List<FeignMethod> methods = clazz.getMethods();
          for (FeignMethod m : methods) {
            for (JavaMethod jm : javaClass.getMethods()) {
              if (Objects.equals(m.getName(), jm.getName()) && null != jm.getComment()) {
                m.setComment(jm.getComment());
              }
            }
          }
        }
        if (c instanceof FeignModel) {
          FeignModel m = (FeignModel)c;
          if (m.getModelFields() != null) {
            for (FeignModelField f : m.getModelFields()) {
              JavaField fieldByName = javaClass.getFieldByName(f.getName());
              if (fieldByName != null && fieldByName.getComment() != null) {
                f.setComment(fieldByName.getComment());
              }
            }
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
}
